#include <iostream>

#include "openglctx.h"

#define CROSSPROD(p1,p2,p3) \
   p3.x = p1.y*p2.z - p1.z*p2.y; \
   p3.y = p1.z*p2.x - p1.x*p2.z; \
   p3.z = p1.x*p2.y - p1.y*p2.x;

OpenGLctx::OpenGLctx(QWidget *parent) : QGLWidget(parent)
{
    rotationX = -21.0;
    rotationY = -57.0;
    rotationZ = 0.0;
    distance = 2.0;

    light_pos[0] = 5.0;
    light_pos[1] = 5.0;
    light_pos[2] = 5.0;

    mesh = new vector<triangle3d>;
    for(int i = -5; i < 5; i++)
    {
	for(int j = -5; j < 5; j++)
	{
	    triangle3d tmp;
	    tmp.points[0].coords[0] = i;
	    tmp.points[0].coords[1] = j;
	    tmp.points[0].coords[2] = sin(float(i + j) / 2);
	    tmp.points[1].coords[0] = i;
	    tmp.points[1].coords[1] = j + 1;
	    tmp.points[1].coords[2] = sin(float(i + j + 1) / 2);
	    tmp.points[2].coords[0] = i + 1;
	    tmp.points[2].coords[1] = j;
	    tmp.points[2].coords[2] = sin(float(i + j + 1) / 2);

	    mesh->push_back(tmp);

	    tmp.points[0].coords[0] = i + 1;
	    tmp.points[0].coords[1] = j + 1;
	    tmp.points[0].coords[2] = sin(float(i + j + 2) / 2);
	    tmp.points[1].coords[0] = i + 1;
	    tmp.points[1].coords[1] = j;
	    tmp.points[1].coords[2] = sin(float(i + j + 1) / 2);
	    tmp.points[2].coords[0] = i;
	    tmp.points[2].coords[1] = j + 1;
	    tmp.points[2].coords[2] = sin(float(i + j + 1) / 2);

	    mesh->push_back(tmp);
	}
    }

}

void OpenGLctx::MakeLighting(void)
{
   GLfloat fullambient[4] = {0.4f, 0.4f, 0.4f, 1.0f};
   GLfloat position[4] = {0.0f, 0.0f, 3.0f, 0.0f};
   GLfloat ambient[4]  = {0.2f, 0.2f, 0.2f, 1.0f};
   GLfloat diffuse[4]  = {0.3f, 0.3f, 0.7f, 1.0f};
   GLfloat specular[4] = {1.0f, 0.0f, 0.0f, 1.0f};

   /* Turn off all the lights */
   glDisable(GL_LIGHT0);
   glDisable(GL_LIGHT1);
   glDisable(GL_LIGHT2);
   glDisable(GL_LIGHT3);
   glDisable(GL_LIGHT4);
   glDisable(GL_LIGHT5);
   glDisable(GL_LIGHT6);
   glDisable(GL_LIGHT7);
   glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER,GL_TRUE);
   glLightModeli(GL_LIGHT_MODEL_TWO_SIDE,GL_FALSE);

   /* Turn on the appropriate lights */
   glLightModelfv(GL_LIGHT_MODEL_AMBIENT,fullambient);
   glLightfv(GL_LIGHT0,GL_POSITION,position);
   glLightfv(GL_LIGHT0,GL_AMBIENT,ambient);
   glLightfv(GL_LIGHT0,GL_DIFFUSE,diffuse);
   glLightfv(GL_LIGHT0,GL_SPECULAR,specular);
   glEnable(GL_LIGHT0);

   /* Sort out the shading algorithm */
   glShadeModel(GL_FLAT);

   /* Turn lighting on */
   glEnable(GL_LIGHTING);
}

void OpenGLctx::initializeGL()
{
    float ambient[4] = {0.3, 0.3, 0.3, 1};
    float light_spot[3] = {0, 0, 0};

    glClearColor(0,0,0,0);
    glShadeModel(GL_FLAT);
    glEnable(GL_DEPTH_TEST);
    glEnable(GL_CULL_FACE);
    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
    glLightfv(GL_LIGHT0, GL_POSITION, light_pos);
    glLightfv(GL_LIGHT0, GL_SPOT_DIRECTION, light_spot);
    glLightfv(GL_LIGHT0, GL_DIFFUSE, ambient);
    glLightfv(GL_LIGHT0, GL_AMBIENT, ambient);
    glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambient);

    glFrontFace(GL_CW);
    glClearColor(0.0,0.0,0.0,0.0);
    glClearAccum(0.0,0.0,0.0,0.0);   /* The default */
    glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);
    glEnable(GL_COLOR_MATERIAL);

}

void OpenGLctx::resizeGL(int width, int height)
{
    gl_width = width;
    gl_height = height;
    glViewport(0, 0, width, height);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    GLfloat x = GLfloat(width) / height;
    glFrustum(-x, x, -1.0, 1.0, 4.0, 1500.0);
    glMatrixMode(GL_MODELVIEW);
}

void OpenGLctx::paintGL()
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    draw();
}


void OpenGLctx::draw()
{
    /*
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glFrustum(-gl_width / gl_height, gl_width / gl_height, -1.0, 1.0, distance * 0.8, distance * 1.1);
*/
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glTranslatef(0.0, 0.0, -distance);
    glTranslatef(0.0, 0.0, -10.0);
    glRotatef(rotationX, 1.0, 0.0, 0.0);
    glRotatef(rotationY, 0.0, 1.0, 0.0);
    glRotatef(rotationZ, 0.0, 0.0, 1.0);

    // axes
    glBegin(GL_LINES);
    glColor3f(1.0, 0.0, 0.0);
    glVertex3f(0.0, 0.0, 0.0);
    glVertex3f(1.0, 0.0, 0.0);

    glColor3f(0.0, 1.0, 0.0);
    glVertex3f(0.0, 0.0, 0.0);
    glVertex3f(0.0, 1.0, 0.0);

    glColor3f(0.0, 0.0, 1.0);
    glVertex3f(0.0, 0.0, 0.0);
    glVertex3f(0.0, 0.0, 1.0);
    glEnd();


    glBegin(GL_TRIANGLES);
    for(vector<triangle3d>::iterator it = mesh->begin(); it != mesh->end(); ++it)
    {
	glColor3f(1.0, 1.0, 1.0);

	for(int j = 0; j < 3; j++)
	    glVertex3f(
		    it->points[j].coords[0] * distance / 10.0,
		    it->points[j].coords[1] * distance / 10.0,
		    it->points[j].coords[2] * distance / 10.0);

    }
    glEnd();

    MakeLighting();
}

void OpenGLctx::mousePressEvent(QMouseEvent *event)
{
    lastPos = event->pos();
}

void OpenGLctx::mouseMoveEvent(QMouseEvent *event)

{
    GLfloat dx = GLfloat(event->x() - lastPos.x()) / width();
    GLfloat dy = GLfloat(event->y() - lastPos.y()) / height();

    if (event->buttons() & Qt::LeftButton)
    {
	rotationX += 180 * dy;
	rotationY += 180 * dx;
	updateGL();
    }
    else if (event->buttons() & Qt::RightButton)
    {
	rotationX += 180 * dy;
	rotationZ += 180 * dx;
	updateGL();
    }
    lastPos = event->pos();
}

void OpenGLctx::wheelEvent(QWheelEvent *event)
{
    distance -= ((float)event->delta() / 360.0);
    updateGL();
    std::cout << "distance is" << distance << endl;
}

void OpenGLctx::setMesh(vector<triangle3d> &x)
{
    mesh->clear();
    mesh->insert(mesh->begin(), x.begin(), x.end());

    updateGL();
}

